﻿#region Using directives

using System;
using System.IO;
using System.Text;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;

using Weazel.Gezel.Conversions.Model;
using SoftwareType = Weazel.Gezel.Conversions.Model.Type;

#endregion

namespace Weazel.Gezel.Conversions.CodeGeneration
{
  public static class CodeGenerator
  {

    public static SourceFile[] GenerateSourceCode(SoftwareModel softwareModel, TargetLanguage language)
    {
			if (language == TargetLanguage.CSharp)
			{
			  return generateSourceCodeHelper(softwareModel, CodeDomProvider.CreateProvider("CSharp"));
			}
			if (language == TargetLanguage.IsoCpp)
			{
				return generateIsoCppSourceCodeHelper(softwareModel);
			}
			if (language == TargetLanguage.VisualC)
			{
				return generateSourceCodeHelper(softwareModel, new Microsoft.VisualC.CppCodeProvider());
			}
			else
			{
			  throw new NotImplementedException("unsupported target language");
			}
    }

		private static SourceFile[] generateSourceCodeHelper(SoftwareModel softwareModel, CodeDomProvider provider)
		{
		  SoftwareType[] types = softwareModel.GetAllTypes();
		  List<SourceFile> sourceFiles = new List<SourceFile>(types.Length);

			StringWriter buffer = new StringWriter();

			CodeGeneratorOptions cgo = new CodeGeneratorOptions();
			// from msdn.microsoft.com:
			// The default value of this property is Block, which indicates 
			// that code should be generated so that braces start on the same 
			// line as the statement or declaration that they are associated 
			// with. A property value of C indicates that braces start on the 
			// line following the statement or declaration that they are 
			// associated with.
			cgo.BracingStyle = "C";
			cgo.ElseOnClosing = false;

		  foreach (SoftwareType type in types)
		  {
				provider.GenerateCodeFromType(type, buffer, cgo);
		    sourceFiles.Add(new SourceFile(type, buffer.ToString()));
				
				// clear buffer
				buffer.GetStringBuilder().Remove(0, buffer.GetStringBuilder().Length);
		  }

		  return sourceFiles.ToArray();
		}

		private static SourceFile[] generateIsoCppSourceCodeHelper(SoftwareModel softwareModel)
		{
			SoftwareType[] types = softwareModel.GetAllTypes();
			List<SourceFile> sourceFiles = new List<SourceFile>(types.Length);



			ICodeGenerator headerProvider = new MiniCCodeProvider.MiniCHeaderGenerator();
			ICodeGenerator codeProvider = new MiniCCodeProvider.MiniCCodeGenerator();

			// we dont use any codegeneration options
			// because our own codegenerator does not 
			// use them anyways right now.
			foreach (SoftwareType type in types)
			{
				StringWriter headerFileBuffer = new StringWriter();
				StringWriter codeFileBuffer = new StringWriter();

				headerProvider.GenerateCodeFromType(type, headerFileBuffer, null);
				
				// create header file part
				//Console.WriteLine("Creating header file for type {0}", type.Name);
				sourceFiles.Add(new SourceFile(type, headerFileBuffer.ToString(), true));

				codeProvider.GenerateCodeFromType(type, codeFileBuffer, null);

				// create code file part
				//Console.WriteLine("Creating code file for type {0}", type.Name);
				sourceFiles.Add(new SourceFile(type, codeFileBuffer.ToString(), false));

				// clear buffer
				headerFileBuffer.GetStringBuilder().Remove(0, headerFileBuffer.GetStringBuilder().Length);

			}

			return sourceFiles.ToArray();
		}
  }
}
